{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6bcc507b-4715-4226-a516-72a78bca4b7a",
   "metadata": {},
   "source": [
    "# 摄像头 demo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f10858f-1ed7-48a6-b4d1-dab604a6072b",
   "metadata": {},
   "source": [
    "## 摄像头实时图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ccc8fbc4-19cf-42ee-99f8-d42657393dbd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "fd2e9345a0da4e49891e032ac27f6333",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Button(button_style='danger', description='停止', style=ButtonStyle())"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f703aa528d81427bae9a2c39a5dccbea",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Image(value=b'')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import ipywidgets as widgets\n",
    "from IPython.display import display\n",
    "import asyncio\n",
    "\n",
    "button = widgets.Button(\n",
    "    description='停止',\n",
    "    button_style='danger', \n",
    ")\n",
    "image = widgets.Image()\n",
    "display(button, image)\n",
    "\n",
    "# Jupyter 图像显示\n",
    "def imshow(img):\n",
    "    _, jpeg = cv2.imencode('.jpeg', img)\n",
    "    image.value = jpeg.tobytes()\n",
    "\n",
    "video = cv2.VideoCapture(0)\n",
    "\n",
    "async def display_image():\n",
    "    try:\n",
    "        while True:\n",
    "            ret, img = video.read()\n",
    "            if not ret:\n",
    "                break\n",
    "            imshow(img)\n",
    "            await asyncio.sleep(0.1)\n",
    "\n",
    "    except asyncio.CancelledError:\n",
    "        pass\n",
    "    finally:\n",
    "        video.release()\n",
    "        \n",
    "task = asyncio.ensure_future(display_image())\n",
    "\n",
    "def on_btn_clicked(b):\n",
    "    task.cancel()\n",
    "\n",
    "button.on_click(on_btn_clicked)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03337066-1832-4c1c-af69-46e73b2c4e6c",
   "metadata": {},
   "source": [
    "## 颜色识别"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "6dd1a01d-01d2-4781-8b8a-d785a6898df9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "56fe3baf79844ba49958c61845d57c3d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Button(button_style='danger', description='停止', style=ButtonStyle())"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "85b664fb69e2411fb0ad42a9e9366994",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(100, 124), description='H:', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ee936eb9482747a2b44076e862841be0",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(43, 255), description='S:', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "20151a7c5455458e97f7a39d903111a8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(46, 255), description='V:', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "579cf1e6bdc547bd87969b24fd8b82eb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(2000, 30000), description='面积:', layout=Layout(width='400px'), max=100000, step=100)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1c782ca25f004a84b76c93ffc5df0d30",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Dropdown(description='摄像头图像', index=3, options=(('颜色提取', 0), ('锐化', 1), ('形态学', 2), ('形状识别', 3)), value=3)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3573b422ec00489abf43037103520d6b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Image(value=b'')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import ipywidgets as widgets\n",
    "import numpy as np\n",
    "from IPython.display import display\n",
    "import asyncio\n",
    "\n",
    "video = cv2.VideoCapture(0)\n",
    "\n",
    "button = widgets.Button(\n",
    "    description='停止',\n",
    "    button_style='danger', \n",
    ")\n",
    "h_slider = widgets.IntRangeSlider(\n",
    "    value=[100, 124],\n",
    "    min=0,\n",
    "    max=255,\n",
    "    description='H:',\n",
    ")\n",
    "s_slider = widgets.IntRangeSlider(\n",
    "    value=[43, 255],\n",
    "    min=0,\n",
    "    max=255,\n",
    "    description='S:',\n",
    ")\n",
    "v_slider = widgets.IntRangeSlider(\n",
    "    value=[46, 255],\n",
    "    min=0,\n",
    "    max=255,\n",
    "    description='V:',\n",
    ")\n",
    "area_slider = widgets.IntRangeSlider(\n",
    "    value=[2000, 30000],\n",
    "    min=0,\n",
    "    max=100000,\n",
    "    step=100,\n",
    "    layout=widgets.Layout(width='400px'),\n",
    "    description='面积:',\n",
    ")\n",
    "dropdown = widgets.Dropdown(\n",
    "    options=[('颜色提取', 0), ('锐化', 1), ('形态学', 2), ('形状识别', 3)],\n",
    "    value=3,\n",
    "    description='摄像头图像',\n",
    ")\n",
    "image = widgets.Image()\n",
    "display(button, h_slider, s_slider, v_slider, area_slider, dropdown, image)\n",
    "\n",
    "# Jupyter 图像显示\n",
    "def imshow(img):\n",
    "    _, jpeg = cv2.imencode('.jpeg', img)\n",
    "    image.value = jpeg.tobytes()\n",
    "\n",
    "async def detect_color():\n",
    "    try:\n",
    "        while True:\n",
    "            ret, img = video.read()\n",
    "            if not ret:\n",
    "                break\n",
    "\n",
    "            lower_hsv = np.array([h_slider.value[0], s_slider.value[0], v_slider.value[0]])\n",
    "            upper_hsv = np.array([h_slider.value[1], s_slider.value[1], v_slider.value[1]])\n",
    "                \n",
    "            # 颜色遮罩\n",
    "            hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
    "            mask = cv2.inRange(hsv, lower_hsv, upper_hsv)\n",
    "\n",
    "            # 锐化\n",
    "            sharpen_kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])\n",
    "            sharpen = cv2.filter2D(mask, -1, sharpen_kernel)\n",
    "\n",
    "            # 形态学变换\n",
    "            kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))\n",
    "            close = cv2.morphologyEx(sharpen, cv2.MORPH_OPEN, kernel, iterations=2)\n",
    "\n",
    "            # 提取边缘\n",
    "            cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "            cnts = cnts[0] if len(cnts) == 2 else cnts[1]\n",
    "\n",
    "            min_area = area_slider.value[0]\n",
    "            max_area = area_slider.value[1]\n",
    "            for c in cnts:\n",
    "                area = cv2.contourArea(c)\n",
    "                # 按大小筛选\n",
    "                if area > min_area and area < max_area:\n",
    "                    x,y,w,h = cv2.boundingRect(c)\n",
    "                    cv2.putText(img, \"center: ({}, {})\".format(x+w/2, y+h/2), (x, y-20), cv2.FONT_HERSHEY_COMPLEX, 1, (218, 218, 3), 1)\n",
    "                    cv2.rectangle(img, (x, y), (x + w, y + h), (218,218,3), 2)\n",
    "            \n",
    "            if dropdown.value == 0:\n",
    "                imshow(mask)\n",
    "            elif dropdown.value == 1:\n",
    "                imshow(sharpen)\n",
    "            elif dropdown.value == 2:\n",
    "                imshow(close)\n",
    "            else:\n",
    "                imshow(img)\n",
    "            await asyncio.sleep(0.1)\n",
    "\n",
    "    except asyncio.CancelledError:\n",
    "        pass\n",
    "    finally:\n",
    "        video.release()\n",
    "        \n",
    "task = asyncio.ensure_future(detect_color())\n",
    "\n",
    "def on_btn_clicked(b):\n",
    "    task.cancel()\n",
    "\n",
    "button.on_click(on_btn_clicked)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf848f9d-a9f7-46de-a770-6a9a24836f78",
   "metadata": {},
   "source": [
    "## 形状识别"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "f1934c6a-b4c3-4a31-8568-7095b55f72af",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f6e9df4b10a24d7188540537443babb1",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Button(button_style='danger', description='停止', style=ButtonStyle())"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9851cf62cb604d59b49561997912c547",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(127, 255), description='二值化范围:', max=255)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a2691315cb914426aba2c39756784bfd",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "IntRangeSlider(value=(8000, 17000), description='面积:', layout=Layout(width='400px'), max=100000, step=100)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8cb5a47b1afe4af0af4c383d35a6121f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Dropdown(description='摄像头图像', index=3, options=(('二值化', 0), ('锐化', 1), ('形态学', 2), ('形状识别', 3)), value=3)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b1b4a8656d4946ab902cd8a94d8a25f4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Image(value=b'')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import ipywidgets as widgets\n",
    "import numpy as np\n",
    "from IPython.display import display\n",
    "import asyncio\n",
    "\n",
    "video = cv2.VideoCapture(0)\n",
    "\n",
    "button = widgets.Button(\n",
    "    description='停止',\n",
    "    button_style='danger', \n",
    ")\n",
    "slider = widgets.IntRangeSlider(\n",
    "    value=[127, 255],\n",
    "    min=0,\n",
    "    max=255,\n",
    "    description='二值化范围:',\n",
    ")\n",
    "area_slider = widgets.IntRangeSlider(\n",
    "    value=[8000, 17000],\n",
    "    min=0,\n",
    "    max=100000,\n",
    "    step=100,\n",
    "    layout=widgets.Layout(width='400px'),\n",
    "    description='面积:',\n",
    ")\n",
    "dropdown = widgets.Dropdown(\n",
    "    options=[('二值化', 0), ('锐化', 1), ('形态学', 2), ('形状识别', 3)],\n",
    "    value=3,\n",
    "    description='摄像头图像',\n",
    ")\n",
    "image = widgets.Image()\n",
    "display(button, slider, area_slider, dropdown, image)\n",
    "\n",
    "# Jupyter 图像显示\n",
    "def imshow(img):\n",
    "    _, jpeg = cv2.imencode('.jpeg', img)\n",
    "    image.value = jpeg.tobytes()\n",
    "\n",
    "async def detect_shape():\n",
    "    try:\n",
    "        while True:\n",
    "            ret, img = video.read()\n",
    "            if not ret:\n",
    "                break\n",
    "            # 二值化\n",
    "            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
    "            _, mask = cv2.threshold(gray, slider.value[0], slider.value[1], cv2.THRESH_BINARY)\n",
    "\n",
    "            # 锐化\n",
    "            sharpen_kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])\n",
    "            sharpen = cv2.filter2D(mask, -1, sharpen_kernel)\n",
    "\n",
    "            # 形态学变换\n",
    "            kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))\n",
    "            close = cv2.morphologyEx(sharpen, cv2.MORPH_OPEN, kernel, iterations=2)\n",
    "\n",
    "            # 提取边缘\n",
    "            cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "            cnts = cnts[0] if len(cnts) == 2 else cnts[1]\n",
    "\n",
    "            min_area = area_slider.value[0]\n",
    "            max_area = area_slider.value[1]\n",
    "            for c in cnts:\n",
    "                area = cv2.contourArea(c)\n",
    "                # 按大小筛选\n",
    "                if area > min_area and area < max_area:\n",
    "                    epsilon = 0.012 * cv2.arcLength(c, True)\n",
    "                    approx = cv2.approxPolyDP(c, epsilon, True)\n",
    "\n",
    "                    # 分析几何形状\n",
    "                    corners = len(approx)\n",
    "                    x,y,w,h = cv2.boundingRect(c)\n",
    "                    if corners >= 7:\n",
    "                        cv2.putText(img, \"shape: circle\", (x, y-30), cv2.FONT_HERSHEY_COMPLEX, 1, (218,218,3), 1)\n",
    "                    else:\n",
    "                        cv2.putText(img, \"sides: {}\".format(corners), (x, y-30), cv2.FONT_HERSHEY_COMPLEX, 1, (218,218,3), 1)\n",
    "                    cv2.putText(img, \"center: ({}, {})\".format(x+w/2, y+h/2), (x, y-60), cv2.FONT_HERSHEY_COMPLEX, 1, (218,218,3), 1)\n",
    "                    cv2.drawContours(img, c, -1, (218,218,3),2)\n",
    "\n",
    "            if dropdown.value == 0:\n",
    "                imshow(mask)\n",
    "            elif dropdown.value == 1:\n",
    "                imshow(sharpen)\n",
    "            elif dropdown.value == 2:\n",
    "                imshow(close)\n",
    "            else:\n",
    "                imshow(img)\n",
    "            await asyncio.sleep(0.1)\n",
    "\n",
    "    except asyncio.CancelledError:\n",
    "        pass\n",
    "    finally:\n",
    "        video.release()\n",
    "\n",
    "task = asyncio.ensure_future(detect_shape())\n",
    "\n",
    "def on_btn_clicked(b):\n",
    "    task.cancel()\n",
    "\n",
    "button.on_click(on_btn_clicked)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1f186cff-1002-462e-9253-289a66c2fa0b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
